package com.runa.monitor.platinterface.platService.gateway;

import com.alibaba.fastjson.JSON;
import com.runa.monitor.comm.StormAIData.DownstreamMessage;
import com.runa.monitor.comm.StormAIData.Param;
import com.runa.monitor.comm.dto.*;
import com.runa.monitor.comm.dto.newComm.*;
import com.runa.monitor.comm.entity.CommNetEquipment;
import com.runa.monitor.comm.enums.RegionTypeEnum;
import com.runa.monitor.platinterface.mqtt.MqttUtil;
import com.runa.monitor.platinterface.mqtt.MqttBaseController;
import com.runa.monitor.platinterface.platService.NetEquService;
import com.runa.monitor.platinterface.platService.RepositoryService;
import com.runa.monitor.platinterface.platService.gateway.pojo.HouseStormData;
import com.runa.monitor.platinterface.platService.gateway.pojo.PanelData;
import com.runa.monitor.platinterface.platService.gateway.pojo.StormBalanceDTO;
import com.runa.monitor.platinterface.platService.pojo.GatewayDeviceParameter;
import com.runa.persistence.SerializeUtil;
import com.runa.persistence.util.LogUtil;
import com.runa.protocol.util.UtilTime;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;


@Slf4j
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class BuildGatewayService {

    @Resource
    private NetEquService netEquService;

    @Resource
    private RepositoryService repositoryService;

    private String topic = "";
    private String taskNo = "";
    private String holdInfo = "";
    private String content = "";

    private Boolean judgmentOnline(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, String topic) {
        if (!netEquService.getAiStormOnOff(commNetEquipment.getSerial())) {
            AsynResult asynResult = new AsynResult();
            asynResult.setHoldInfo(holdInfo);
            asynResult.setTask(taskNo);
            asynResult.offlineSetting();
            asynResult.setIdentity(commNetEquipment.getId());
            MqttUtil.send(topic, asynResult);
            return false;
        }
        return true;
    }

    private Boolean judgmentOnline(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment, String topic) {
        if (!netEquService.getAiStormOnOff(commNetEquipment.getSerial())) {
            AsynResult asynResult = new AsynResult();
            asynResult.setHoldInfo(holdInfo);
            asynResult.setTask(taskNo);
            asynResult.offlineSetting();
            if (nodeList==null){
                asynResult.setIdentity(commNetEquipment.getId());
                MqttUtil.send(topic, asynResult);
            }else {
                for (Node node : nodeList) {
                    asynResult.setIdentity(node.getId());
                    MqttUtil.send(topic, asynResult);
                }
            }
            return false;
        }
        return true;
    }

    public void rccReadNodeData(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            this.content = node.getId();
            DownstreamMessage message = new DownstreamMessage();
            message.setId(commNetEquipment.getSerial());
            message.setSeq(taskNo + "-" + holdInfo + "-" + node.getId());//这里新增第三个属性
            message.setTime((int) (System.currentTimeMillis() / 1000));
            Param param = new Param();
            param.setType("rddevice");
            Map<String, Object> data = new HashMap<>();
            String deviceType;
            if (node instanceof Meter) {
                deviceType = ((Meter) node).getRegion() == RegionTypeEnum.户 ? "housemeter" : "buildmeter";
            } else if (node instanceof Valve) {
                deviceType = ((Valve) node).getRegion() == RegionTypeEnum.户 ? "housevalve" : "buildvalve";
            } else if (node instanceof TempPanel) {
                deviceType = "temp";
            } else {
                throw new IllegalStateException("Unexpected value: " + node.getSerial());
            }
            data.put("Type", deviceType);
            data.put("Addr", node.getSerial());
            data.put("Protocol", node.getStatuteCode());
            data.put("Port", node.getPort());
            data.put("Baud", 2400);
            data.put("Parity", "E");
            param.setData(data);
            message.setParam(param);
            GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
            sendRequest(commNetEquipment, message);
        }
    }

    private String getTopicOfModel(String model) {
        if ("TYMT-AI-RN".equalsIgnoreCase(model)) {
            return "/aigw_mt/";
        } else if ("TYJY-AI-RN".equalsIgnoreCase(model)) {
            return "/aigw_jy/";
        } else if ("TYHMY-AI-RN".equalsIgnoreCase(model)) {
            return "/aigw_hmy/";
        } else {
            return "/aigw/";
        }
    }

    private void sendRequest(CommNetEquipment commNetEquipment, DownstreamMessage message) {
        try {
            log.info("网关---下发消息：" + JSON.toJSONString(message));
            MqttBaseController.getConnector().sendMessage(getTopicOfModel(commNetEquipment.getModel()) + commNetEquipment.getSerial() + "/down", message);
            int timeout = commNetEquipment.getTimeout() == null ? 5000 : commNetEquipment.getTimeout();
            if (!mockLock(timeout)) {
                int maxRs = 1;
                if (retrySign >= maxRs) {
                    log.info("网关——{} 指令超时", commNetEquipment.getSerial());
                    AsynResult asynResult = new AsynResult();
                    asynResult.setHoldInfo(holdInfo);
                    asynResult.setTask(taskNo);
                    asynResult.setIdentity(content);
                    asynResult.timeoutSetting();
                    LogUtil.getLogAndInfo(commNetEquipment.getSerial(), "MQTT",  "{} {}", topic, JSON.toJSONString(asynResult));
                    MqttUtil.send(topic, asynResult);
                    GatewayMessageTrans.buildGatewayServiceHashMap.remove(message.getSeq());
                } else {
                    log.info("网关——{} 指令超时，进行重发", commNetEquipment.getSerial());
                    this.retrySign++;
                    sendRequest(commNetEquipment, message);
                }
            }  //成功消息在监听测

        } catch (Exception e) {
            log.error("SendRequest Error: ", e);
        }
    }

    private int retrySign = 0;
    private Thread sleepThread;

    private boolean mockLock(Integer timeout) {
        this.sleepThread = Thread.currentThread();
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            return true;
        }
        return false;
    }

    public void mockUnlock() {
        if (sleepThread != null &&
                sleepThread.getState()
                        .equals(Thread.State.TIMED_WAITING)) {
            sleepThread.interrupt();
        }
    }

    private DownstreamMessage control(String netSerial) {
        DownstreamMessage message = new DownstreamMessage();
        message.setId(netSerial);
        message.setSeq(taskNo + "-" + holdInfo);
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("ctrl");
        Map<String, Object> data = new HashMap<>();
        data.put("Cmd", "setvalvetempsn");
        param.setData(data);
        message.setParam(param);
        return message;
    }

    private DownstreamMessage controlDevice(String type, String netSerial, String equId, String equSerial, Integer statusCode, Integer port, String cmd, String value) {
        DownstreamMessage message = new DownstreamMessage();
        message.setId(netSerial);
        message.setSeq(taskNo + "-" + holdInfo + "-" + equId);
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("ctrldevice");
        Map<String, Object> data = new HashMap<>();
        data.put("Type", type);
        data.put("Addr", equSerial);
        data.put("Protocol", statusCode);
        data.put("Port", port);
        data.put("Baud", 2400);
        data.put("Parity", "E");
        Map<String, String> ctrl = new HashMap<>();
        ctrl.put("Cmd", cmd);
        ctrl.put("Value", value);
        data.put("Ctrl", ctrl);
        param.setData(data);
        message.setParam(param);
        return message;
    }

    private DownstreamMessage houseValveControlDevice(String netSerial, String equId, String equSerial, Integer statusCode, Integer port, String cmd, String value) {
        return controlDevice("housevalve", netSerial, equId, equSerial, statusCode, port, cmd, value);
    }

    private DownstreamMessage houseValveControlDevice(String netSerial, Node node, String cmd, String value) {
        return houseValveControlDevice(netSerial, node.getId(), node.getSerial(), node.getStatuteCode(), node.getPort(), cmd, value);
    }


    public void houseValveSetRl(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment, String accHeat, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            DownstreamMessage message = houseValveControlDevice(commNetEquipment.getSerial(), node, "setrl", accHeat);
            send(message, commNetEquipment);
        }
    }

    public void houseValveSetTempSn(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment, String panelSn, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            DownstreamMessage message = houseValveControlDevice(commNetEquipment.getSerial(), node, "settempsn", panelSn);
            send(message, commNetEquipment);
        }
    }
    public void houseValveSetValveTempSn(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        DownstreamMessage message = control(commNetEquipment.getSerial());
        send(message, commNetEquipment);
    }


    private void send(DownstreamMessage message, CommNetEquipment commNetEquipment) {
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }


    public void valveOnOffControl(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment,
                                  Boolean ifLock, Integer opening, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            this.content = node.getId();
            DownstreamMessage message = new DownstreamMessage();
            message.setId(commNetEquipment.getSerial());
            message.setSeq(taskNo + "-" + holdInfo + "-" + node.getId());
            message.setTime((int) (System.currentTimeMillis() / 1000));
            Param param = new Param();
            param.setType("ctrldevice");
            Map<String, Object> data = new HashMap<>();
            data.put("Type", "housevalve");
            data.put("Addr", node.getSerial());
            data.put("Protocol", node.getStatuteCode());
            data.put("Port", node.getPort());
            data.put("Baud", 2400);
            data.put("Parity", "E");
            Map<String, String> ctrl = new HashMap<>();
            ctrl.put("Cmd", "setvlock");
            String value = (ifLock ? "1," : "0,") + opening;
            ctrl.put("Value", value);
            data.put("Ctrl", ctrl);
            param.setData(data);
            message.setParam(param);
            GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
            sendRequest(commNetEquipment, message);
        }
    }


    public void valveTemperatureControl(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment,
                                        Float temperature, Boolean ifLock,
                                        Float temperatureUp, Float temperatureDown, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            this.content = node.getId();
            DownstreamMessage message = new DownstreamMessage();
            message.setId(commNetEquipment.getSerial());
            message.setSeq(taskNo + "-" + holdInfo + "-" + node.getId());
            message.setTime((int) (System.currentTimeMillis() / 1000));
            Param param = new Param();
            param.setType("ctrldevice");
            Map<String, Object> data = new HashMap<>();
            data.put("Type", "housevalve");
            data.put("Addr", node.getSerial());
            data.put("Protocol", node.getStatuteCode());
            data.put("Port", node.getPort());
            data.put("Baud", 2400);
            data.put("Parity", "E");
            Map<String, String> ctrl = new HashMap<>();
            if (null != temperature) {
                if (null != temperatureDown && null != temperatureUp) {
                    ctrl.put("Cmd", "settemp");
                    String value = ((ifLock != null && ifLock) ? "1," : "0,") + temperature + "," + temperatureDown + "," + temperatureUp;
                    ctrl.put("Value", value);
                } else {
                    ctrl.put("Cmd", "settlock");
                    String value = ((ifLock != null && ifLock) ? "1," : "0,") + temperature;
                    ctrl.put("Value", value);
                }
            } else if (null != temperatureDown && null != temperatureUp) {
                ctrl.put("Cmd", "settrange");
                String value = temperatureDown + "," + temperatureUp;
                ctrl.put("Value", value);
            }
            data.put("Ctrl", ctrl);
            param.setData(data);
            message.setParam(param);
            GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
            sendRequest(commNetEquipment, message);
        }
    }



    public void buildValveControl(String taskNo, String holdInfo, List<Node> nodeList, CommNetEquipment commNetEquipment,
                                  String s, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, nodeList, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        for (Node node : nodeList) {
            this.content = node.getId();
            DownstreamMessage message = new DownstreamMessage();
            message.setId(commNetEquipment.getSerial());
            message.setSeq(taskNo + "-" + holdInfo + "-" + node.getId());
            message.setTime((int) (System.currentTimeMillis() / 1000));
            Param cmdParam = new Param();
            cmdParam.setType("ctrldevice");
            Map<String, Object> data = new HashMap<>();
            data.put("Type", "buildvalve");
            data.put("Addr", node.getSerial());
            data.put("Protocol", node.getStatuteCode());
            data.put("Port", node.getPort());
            data.put("Baud", 2400);
            data.put("Parity", "E");
            Map<String, String> ctrl = new HashMap<>();
            ctrl.put("Cmd", "setvdeg");
            ctrl.put("Value", s);
            data.put("Ctrl", ctrl);
            cmdParam.setData(data);
            message.setParam(cmdParam);
            GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
            sendRequest(commNetEquipment, message);
        }
    }


    public void ctrlDeviceParameter(String taskNo, String holdInfo, CommNetEquipment commNetEquipment,
                                    GatewayDeviceParameter gatewayDeviceParameter, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, null, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        log.debug("网关---下发网络设备频率");
        DownstreamMessage message = new DownstreamMessage();
        message.setId(commNetEquipment.getSerial());
//            message.setSeq(taskNo.toString() + " - " + valve);
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param cmdParam = new Param();
        cmdParam.setType("setparam");
        Map<String, Object> data = new HashMap<>();
        if (gatewayDeviceParameter.getMinuteOfCollectForBuild() != null)
            data.put("build_collect_interval", gatewayDeviceParameter.getMinuteOfCollectForBuild().toString());
        if (gatewayDeviceParameter.getMinuteOfUploadForBuild() != null)
            data.put("build_upload_interval", gatewayDeviceParameter.getMinuteOfUploadForBuild().toString());
        if (gatewayDeviceParameter.getMinuteOfCollectForHouse() != null)
            data.put("house_collect_interval", gatewayDeviceParameter.getMinuteOfCollectForHouse().toString());
        if (gatewayDeviceParameter.getMinuteOfUploadForHouse() != null)
            data.put("house_upload_interval", gatewayDeviceParameter.getMinuteOfUploadForHouse().toString());
        cmdParam.setData(data);
        message.setParam(cmdParam);
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }

    public void readMeterReadingCycleParameter(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, null, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        log.info("网关---读抄表周期, 设备编号：{}", commNetEquipment.getSerial());
        DownstreamMessage message = new DownstreamMessage();
        message.setId(commNetEquipment.getSerial());
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param cmdParam = new Param();
        cmdParam.setType("getparam");
        cmdParam.setData(new ArrayList<>());
        message.setParam(cmdParam);
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }


    //add
    public void issuedHouseModelParam(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, HouseStormData stormData
            , String topic) {
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        DownstreamMessage message = new DownstreamMessage();
        message.setFunc("down");
        message.setId(commNetEquipment.getSerial());
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("aimodelparam");
        Map<String, Object> data = new HashMap<>();
        data.put("ai_onoff", stormData.getAi_onoff());
        data.put("updateTime", UtilTime.getCurrentTime("yyyy-MM-dd HH:mm:ss"));
        data.put("targetAvgTemp", stormData.getTargetAvgTemp());
        Map<String, Object> branchParams = new HashMap<>();
        branchParams.put(stormData.getStationBranchName(), data);
        param.setData(branchParams);
        message.setParam(param);
        log.info("【全网平衡-户】模型参数下发--网关：{}，消息：{}", commNetEquipment.getSerial(), SerializeUtil.obj2json(message));
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }


    public void issuedHouseBalanceModelParam(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, StormBalanceDTO dto
            , String topic) {
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        DownstreamMessage message = new DownstreamMessage();
        message.setFunc("down");
        message.setId(commNetEquipment.getSerial());
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("house_aimodelparam");
        List<Map<String, Object>> dataArray = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        dto.getBalanceAndIds().forEach(balanceAndId -> {
            Map<String, Object> balance = new HashMap<>();
            balance.put("balanceOff", balanceAndId.getBalanceOff() ? 1 : 0);
            data.put(balanceAndId.getSerialNo(), balance);
        });
        dataArray.add(data);
        param.setData(dataArray);
        message.setParam(param);
        Integer timeout = repositoryService.getCommNetEquipmentByNetSerial(message.getId()).getTimeout();
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }

    public void issuedPanelParam(String taskNo, String holdInfo, CommNetEquipment commNetEquipment, PanelData panelData
            , String topic) {
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        Map<String, Object> result = new HashMap<>();
        DownstreamMessage message = new DownstreamMessage();
        message.setFunc("down");
        message.setId(commNetEquipment.getSerial());
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("paneldata");
        Map<String, Object> data = new HashMap<>();
        data.put("Sn", panelData.getSn());
        data.put("Temp", panelData.getTemp());
        data.put("Ver", panelData.getVer());
        data.put("Rssi", panelData.getRssi());
        data.put("Vbat", panelData.getVbat());
        data.put("State", panelData.getState());
        param.setData(data);
        message.setParam(param);
        Integer timeout = repositoryService.getCommNetEquipmentByNetSerial(message.getId()).getTimeout();
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }


//    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    /**
     * 档案下发
     */
    public void meterDownloadFile(String taskNo, String holdInfo, CommNetEquipment commNetEquipment,
                                  String path, String MD5, String topic) {
        if (!judgmentOnline(taskNo, holdInfo, null, commNetEquipment, topic)) return;
        this.taskNo = taskNo;
        this.holdInfo = holdInfo;
        this.topic = topic;
        this.content = commNetEquipment.getId();
        DownstreamMessage message = new DownstreamMessage();
        message.setFunc("down");
        message.setId(commNetEquipment.getSerial());
        message.setSeq(taskNo + "-" + holdInfo + "-" + "00000000");
        message.setTime((int) (System.currentTimeMillis() / 1000));
        Param param = new Param();
        param.setType("filedownload");
        Map<String, Object> data = new HashMap<>(2);
        data.put("Title", "archives");
        data.put("Path", "http://" + path);
        data.put("Md5", MD5);
        data.put("Timeout", "300");
        data.put("Lag", "10");
        param.setData(data);
        message.setParam(param);
        Integer timeout = repositoryService.getCommNetEquipmentByNetSerial(message.getId()).getTimeout();
        GatewayMessageTrans.buildGatewayServiceHashMap.put(message.getSeq(), this);
        sendRequest(commNetEquipment, message);
    }


}
